#include "LecteurProgramme.h"

#include <iostream>
using namespace std;

////////////////////////////////////////////////////////////////////////////////

LecteurProgramme::LecteurProgramme(string nomFich) : Lecteur() {
    LecteurSymbole l(nomFich);
    this->setLs(&l);
    arbre = programme();
    cout << "Syntaxe correcte." << endl;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurProgramme::programme() {
    // <programme> ::= <seqDecl> debut <seqInst> fin
    seqDecl();
    sauterSymCour("debut");
    Noeud* si = seqInst();
    sauterSymCour("fin");
    testerSymCour("<FINDEFICHIER>");
    return si;
}


////////////////////////////////////////////////////////////////////////////////

void LecteurProgramme::seqDecl() {
    // <seqDecl> ::= <declFonction> ; { <declFonction> ; }
    while (getLs()->getSymCour() == "fonction") {
        LecteurFonction* lf = new LecteurFonction(ls);
        tf.push_back(lf);
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurProgramme::seqInst() {
    // <seqInst> ::= <inst> ; { <inst> ; }

    NoeudSeqInst* si = new NoeudSeqInst();
    do {
        si->ajouteInstruction(inst());
        sauterSymCour(";");
    } while (getLs()->getSymCour() == "<VARIABLE>"
            || getLs()->getSymCour() == "si"
            || getLs()->getSymCour() == "tantque"
            || getLs()->getSymCour() == "repeter"
            || getLs()->getSymCour() == "lire"
            || getLs()->getSymCour() == "ecrire"
            || getLs()->getSymCour() == "choix");
    // tant que le symbole courant est un debut possible d'instruction...
    return si;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurProgramme::appelFonction() {
    // <appelFonction> ::= <variable> ( <expression> { , <expression> } )
    string nom = getLs()->getSymCour().getChaine();
    getLs()->suivant();
    sauterSymCour("(");
    vector<Noeud*> parametres;
    parametres.push_back(ts.chercheAjoute(getLs()->getSymCour()));
    getLs()->suivant();
    while (getLs()->getSymCour() == ",") {
        getLs()->suivant();
        parametres.push_back(ts.chercheAjoute(getLs()->getSymCour()));
        getLs()->suivant();
    }
    sauterSymCour(")");

    vector<LecteurFonction*>::iterator itFonc = tf.begin();
    bool trouve = false;
    while (!trouve && itFonc != tf.end()) {
        if (*(*itFonc) == nom) {
            trouve = true;
        }
    }
    if (trouve) {
        if ((*itFonc)->getNbParam() != parametres.size()) {
            cout << "Erreur : nombre de paramètres incorrect dans la fonction " << nom << endl;
            cout << "Attendu : " << (*itFonc)->getNbParam() << " ; Trouvé : " << parametres.size() << endl;
            exit(0);
        } else {
            Noeud* appel = new NoeudAppelFonction(nom, parametres, &tf);
            return appel;
        }
    } else {
        cout << "Erreur : fonction " << nom << " non déclarée" << endl;
        exit(0);
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurProgramme::inst() {
    // <inst> ::= <affectation> | <instSi> | <instTq> | <instRepeter> | <instLire> | <instEcrire>
    if (getLs()->getSymCour() == "si") {
        return instSi();
    } else if (getLs()->getSymCour() == "tantque") {
        return instTq();
    } else if (getLs()->getSymCour() == "repeter") {
        return instRepeter();
    } else if (getLs()->getSymCour() == "lire") {
        return instLire();
    } else if (getLs()->getSymCour() == "ecrire") {
        return instEcrire();
    } else if (getLs()->getSymCour() == "choix") {
        return instChoix();
    } else if (getLs()->getSymCour() == "<VARIABLE>") {
        return affectation();
    } else {
        erreur("instruction");
        return NULL;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurProgramme::affectation() {
    // <affectation> ::= <variable> = <expression>

    testerSymCour("<VARIABLE>");
    Noeud* var = ts.chercheAjoute(getLs()->getSymCour());
    getLs()->suivant();
    sauterSymCour("=");
    Noeud* exp = expBool();
    return new NoeudAffectation(var, exp);
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurProgramme::instSi()
//  <instSi> ::= si ( <expBool> ) <seqInst> { sinonsi ( <expBool> ) <seqInst> } [ sinon <seqInst> ] finsi
{
    // Création du si avec le premier cas
    sauterSymCour("si");
    sauterSymCour("(");
    Noeud* condSi = expBool();
    sauterSymCour(")");
    Noeud* seqInstSi = seqInst();
    Noeud* instSi = new NoeudInstSi(condSi, seqInstSi);

    // Ajout des cas suivants si existants
    while (getLs()->getSymCour() == "sinonsi") {
        sauterSymCour("sinonsi");
        sauterSymCour("(");
        Noeud* cond = expBool();
        sauterSymCour(")");
        Noeud* seq = seqInst();
        ((NoeudInstSi*) instSi)->ajouterCas(cond, seq);
    }
    if (getLs()->getSymCour() == "sinon") {
        sauterSymCour("sinon");
        ((NoeudInstSi*) instSi)->ajouterCas(NULL, seqInst());
    }
    sauterSymCour("finsi");
    return instSi;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurProgramme::instChoix()
//  <instChoix> ::= choix ( <expression> ) { cas <expression> : <seqInst> stop } defaut : <seqInst> stop finchoix
{
    // Création du choix
    Noeud* instChoix = new NoeudInstChoix();
    sauterSymCour("choix");
    sauterSymCour("(");
    Noeud* elem = expression();
    sauterSymCour(")");
    while (getLs()->getSymCour() == "cas") {
        sauterSymCour("cas");
        Noeud* valeur = expression();
        sauterSymCour(":");
        Noeud* seq = seqInst();
        Noeud* cond = new NoeudOperateurBinaire(Symbole("=="), elem, valeur);
        ((NoeudInstChoix*) instChoix)->ajouterCas(cond, seq);
        sauterSymCour("stop");
    }
    sauterSymCour("defaut");
    sauterSymCour(":");
    Noeud* seq = seqInst();
    ((NoeudInstChoix*) instChoix)->ajouterCas(NULL, seq);
    sauterSymCour("stop");
    sauterSymCour("finchoix");
    return instChoix;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurProgramme::instTq()
//  <instTq> ::= tantque ( <expBool> ) <seqInst> fintq
{
    sauterSymCour("tantque");
    sauterSymCour("(");
    Noeud* cond = expBool();
    sauterSymCour(")");
    Noeud* seq = seqInst();
    sauterSymCour("fintq");
    Noeud* instTq = new NoeudInstTq(cond, seq);
    return instTq;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurProgramme::instRepeter()
//  <instRepeter> ::= repeter <seqInst> jusqua ( <expBool> )
{
    sauterSymCour("repeter");
    Noeud* seq = seqInst();
    sauterSymCour("jusqua");
    sauterSymCour("(");
    Noeud* cond = expBool();
    sauterSymCour(")");
    Noeud * instRepeter = new NoeudInstRepeter(cond, seq);
    return instRepeter;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurProgramme::instPour()
//  <instPour> ::= pour ( <affectation> ; <expBool> ; <affectation> ) <seqInst> finpour
{
    sauterSymCour("pour");
    sauterSymCour("(");
    Noeud* aff1 = affectation();
    sauterSymCour(";");
    Noeud* cond = expBool();
    sauterSymCour(";");
    Noeud* aff2 = affectation();
    sauterSymCour(")");
    Noeud* seq = seqInst();
    sauterSymCour("finpour");
    Noeud* instPour = new NoeudInstPour(aff1, cond, aff2, seq);
    return instPour;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurProgramme::instLire()
//  <instLire> ::= lire ( <variable> )
{
    sauterSymCour("lire");
    sauterSymCour("(");
    SymboleValue* var = ts.chercheAjoute(getLs()->getSymCour());
    getLs()->suivant();
    sauterSymCour(")");
    Noeud* instLire = new NoeudInstLire(var);
    return instLire;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurProgramme::instEcrire()
//  <instEcrire> ::= ecrire ( <expression> )
{
    sauterSymCour("ecrire");
    sauterSymCour("(");
    Noeud* argExp = expression();
    sauterSymCour(")");
    Noeud* instEcrire = new NoeudInstEcrire(argExp);
    return instEcrire;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurProgramme::facteur() {
    // <facteur> ::= <ENTIER> | <REEL> | <CARACTERE> | <CHAINE> |
    //               <variable> | <opUnaire> <expBool> | ( <expBool> )

    Noeud* fact = NULL;

    if (getLs()->getSymCour() == "<VARIABLE>") {
        vector<LecteurFonction*>::iterator itFonc = tf.begin();
        bool trouve = false;
        while (!trouve && itFonc != tf.end()) {
            if (*(*itFonc) == getLs()->getSymCour().getChaine()) {
                trouve = true;
            }
        }
        if (trouve) {
            fact = appelFonction();
        } else {
            fact = ts.chercheAjoute(getLs()->getSymCour());
            getLs()->suivant();
        }
    } else if (getLs()->getSymCour() == "<ENTIER>"
            || getLs()->getSymCour() == "<REEL>" || getLs()->getSymCour() == "<CARACTERE>"
            || getLs()->getSymCour() == "<CHAINE>") {
        fact = ts.chercheAjoute(getLs()->getSymCour());
        getLs()->suivant();
    } else if (getLs()->getSymCour() == "-" || getLs()->getSymCour() == "non") {
        Symbole operateur = opUnaire();
        Noeud* exp = expBool();
        // on représente le moins unaire (- facteur) par une soustractin binaire (0 - facteur)
        fact = new NoeudOperateurBinaire(operateur, NULL, exp);
    } else if (getLs()->getSymCour() == "(") {
        getLs()->suivant();
        fact = expBool();
        sauterSymCour(")");
    } else
        erreur("<facteur>");
    return fact;
}

